﻿using System;
using System.Collections.Generic;
using System.Reflection;

namespace NAom.Core
{
  public static class InterfaceSupport
  {

    public static IPropertyType[] DerivePropertyTypes(params Type[] interfaceTypes)
    {
      if (interfaceTypes == null) throw new ArgumentNullException("interfaceTypes");

      List<IPropertyType> propertyTypes = new List<IPropertyType>();
      foreach (Type interfaceType in interfaceTypes)
      {
        propertyTypes.AddRange(DerivePropertyTypes(interfaceType));
      }

      return propertyTypes.ToArray();
    }

    public static IPropertyType[] DerivePropertyTypes<TInterface>()
    {
      return DerivePropertyTypes(typeof(TInterface));
    }

    public static IPropertyType[] DerivePropertyTypes(Type interfaceType)
    {
      if (interfaceType == null) throw new ArgumentNullException("interfaceType");
      if (!interfaceType.IsInterface) throw new ArgumentException(SR.InterfaceTypeExpected(interfaceType), "interfaceType");

      IEnumerable<PropertyInfo> propertyInfos = FindProperties(interfaceType, /* includeBaseInterface */ true);
      return CreatePropertyTypes(propertyInfos);
    }

    public static IEnumerable<PropertyInfo> FindProperties(Type interfaceType)
    {
      return FindProperties(interfaceType, /* includeBaseInterface */ false);
    }

    public static IEnumerable<PropertyInfo> FindProperties(Type interfaceType, bool includeBaseInterface)
    {
      if (interfaceType == null) throw new ArgumentNullException("interfaceType");
      if (!interfaceType.IsInterface) throw new ArgumentException(SR.InterfaceTypeExpected(interfaceType), "interfaceType");

      List<PropertyInfo> propertyInfos = new List<PropertyInfo>();

      propertyInfos.AddRange(interfaceType.GetProperties(BindingFlags.Public | BindingFlags.Instance));

      if (includeBaseInterface)
      {
        // inherited interfaces
        foreach (Type parentInterfaceType in interfaceType.GetInterfaces())
        {
          propertyInfos.AddRange(FindProperties(parentInterfaceType));
        }
      }

      return propertyInfos;
    }

    public static IPropertyType[] CreatePropertyTypes(IEnumerable<PropertyInfo> propertyInfoEnumerable)
    {
      if (propertyInfoEnumerable == null) throw new ArgumentNullException("propertyInfoEnumerable");

      Type propertyTypeGenericType = typeof(PropertyType<>);

      List<IPropertyType> propertyTypes = new List<IPropertyType>();
      foreach (PropertyInfo propertyInfo in propertyInfoEnumerable)
      {
        Type propertyTypeType = propertyTypeGenericType.MakeGenericType(propertyInfo.PropertyType);
        IPropertyType propertyType = (IPropertyType)Activator.CreateInstance(propertyTypeType, propertyInfo.Name);
        propertyTypes.Add(propertyType);
      }

      return propertyTypes.ToArray();
    }

  }
}
